﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Ewk.Math.Algebra.UnitTests.MatrixTests.NumericTests
{
    [TestClass]
    public abstract class MultiplyTestBase<T> : NumericTestBase<T>
        where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
    {
        [TestMethod]
        public void Multiply_by_the_Identity_matrix_equals_the_matrix()
        {
            const int m = 13;
            RowCount = m;
            ColumnCount = m;

            var matrixI = Identity;
            var matrix = Matrix;

            var result1 = matrix*matrixI;
            var result2 = matrixI*matrix;

            Assert.AreEqual(result1, result2);
            Assert.AreEqual(result1, matrix);
        }

        [TestMethod]
        public void Multiply_performs_a_multiplication_on_each_element()
        {
            var matrix = Matrix;
            var factor = Two;
            matrix.Multiply(factor);

            var iValue = One;
            for (var i = 0; i < matrix.RowCount; i++)
            {
                var jValue = One;
                for (var j = 0; j < matrix.ColumnCount; j++)
                {
                    Assert.AreEqual((iValue*jValue)*factor, matrix[i, j].Value);
                    jValue++;
                }
                iValue++;
            }
        }

        [TestMethod]
        public void Multiply_performs_a_multiplication_of_two_matrixes()
        {
            /* Matrix 1
                    1   2   3
                    ---------
                1 | 2   2   2
                2 | 2   2   2
                3 | 2   2   2
            */

            const int m = 3;
            var matrix1 = CreateMatrix(m, m, (i, j) => Two);
            var matrix2 = CreateMatrix(m, m, (i, j) => One);

            matrix1.Multiply(matrix2);
            var six = Two*Three;
            for (var i = 0; i < m; i++)
            {
                for (var j = 0; j < m; j++)
                {
                    Assert.AreEqual(six, matrix1[i, j].Value);
                }
            }
        }

        [TestMethod]
        public void Multiply_can_perform_a_multiplication_of_two_differently_sized_matrixes()
        {
            /* Matrix 1
                    1   2   3
                    ---------
                1 | 2   2   2
                2 | 2   2   2
            */

            const int rowCount = 2;
            const int columnCount = 3;
            var matrix1 = CreateMatrix(rowCount, columnCount, (i, j) => Two);
            var matrix2 = Matrix<T>.Transpose(matrix1);

            var result = Matrix<T>.Multiply(matrix1, matrix2);

            Assert.AreEqual(matrix1.RowCount, result.RowCount);
            Assert.AreEqual(matrix2.ColumnCount, result.ColumnCount);

            var twelve = Two*Two*Three;
            for (var i = 0; i < result.RowCount; i++)
            {
                for (var j = 0; j < result.ColumnCount; j++)
                {
                    Assert.AreEqual(twelve, result[i, j].Value);
                }
            }
        }

        [TestMethod]
        public void Multiply_can_perform_a_multiplication_of_a_Matrix_and_a_Vector()
        {
            /* Matrix 1
                    1   2   3
                    ---------
                1 | 2   2   2
                2 | 2   2   2
            */

            const int rowCount = 2;
            const int columnCount = 3;
            var matrix1 = CreateMatrix(rowCount, columnCount, (i, j) => Two);
            var vector = CreateVector(Two, Two, Two);

            var result = matrix1*vector;

            Assert.AreEqual(matrix1.RowCount, result.Length);

            var twelve = Two*Two*Three;
            for (var i = 0; i < result.Length; i++)
            {
                Assert.AreEqual(twelve, result[i].Value);
            }
        }
    }
}